//
// Created on 2025/11/5.
//
// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
// please include "napi/native_api.h".
#include "background_cache_manager.h"
#include "background_cache.h"

BackgroundCacheManager* BackgroundCacheManager::instance_ = nullptr;
std::mutex BackgroundCacheManager::instanceMutex_;


BackgroundCacheManager* BackgroundCacheManager::GetInstance()
{
    PRE_EXPORT_LOGD("create");
    std::lock_guard<std::mutex> lock(instanceMutex_);
    if (instance_ == nullptr) {
        instance_ = new BackgroundCacheManager();
    }
    PRE_EXPORT_LOGI("create %{public}p", instance_);
    return instance_;
}

BackgroundCacheManager::~BackgroundCacheManager()
{
    PRE_EXPORT_LOGI("delete %{public}p", this);
    std::lock_guard<std::mutex> lock(instanceMutex_);
    if (instance_) {
        delete instance_;
        instance_ = nullptr;
    }
}

std::weak_ptr<BackgroundCache> BackgroundCacheManager::RecodeBackgroundCache(
    const std::string &hashString, std::shared_ptr<BackgroundCache> backgroundCache)
{
    PRE_EXPORT_LOGI("add cache: %{public}s", hashString.c_str());
    std::lock_guard<std::mutex> lock(listMutex_);
    backgroundCacheList_.emplace_back(backgroundCache);
    return backgroundCacheList_.back();
}

std::vector<std::weak_ptr<BackgroundCache>> BackgroundCacheManager::GetBackgroundCacheList()
{
    std::lock_guard<std::mutex> lock(listMutex_);
    return backgroundCacheList_;
}

void BackgroundCacheManager::runTask()
{
    PRE_EXPORT_LOGI("start codec encode");
    bool hasCacheToEncode = true;
    while(running_.load()) {
        if (!hasCacheToEncode) {
            running_.store(false);
            PRE_EXPORT_LOGI("stop codec encode for no valid cache");
            return;
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        hasCacheToEncode = false;
        auto backgroundCacheList = GetBackgroundCacheList();
        for(auto cacheWeakPtr: backgroundCacheList) {
            if (!running_.load() || encoder_ == nullptr) {
                PRE_EXPORT_LOGI("stop codec encode or encoder_ is nullptr");
                return;
            }
            auto cache = cacheWeakPtr.lock();
            if (cache == nullptr || cache->IsInterrupt()) {
                PRE_EXPORT_LOGW("cache is invalid");
                continue;
            }
            hasCacheToEncode = true;
            uint64_t start = 0;
            uint64_t end = 0;
            for (ExportType exportType: {ExportType::PRE_EXPORT, ExportType::EXPORT}) {
                bool canEncode = cache->CheckEnCodeRange(&start, &end, exportType);
                if (!canEncode || start > end) {
                    continue;
                }
                PRE_EXPORT_LOGD("to encode start %{public}ld %{public}ld", start, end);
                CodecBufferQueue forwardQueue;
                for(uint64_t frameIndex=start; frameIndex<=end; frameIndex++) {
                    CacheInfo *cacheInfo = cache->GetCacheInfoByIndex(frameIndex, exportType);
                    if (cacheInfo == nullptr) {
                        PRE_EXPORT_LOGE("get frame %{public}ld failed", frameIndex);
                        break;
                    }
                    forwardQueue.Enqueue(std::make_shared<CacheInfo>(*cacheInfo));
                }
                if (forwardQueue.empty()) {
                    continue;
                }
                PRE_EXPORT_LOGD("to codec encode range [%{public}ld, %{public}ld] %{public}d",
                                start, end, forwardQueue.size());
                encoder_->Configure(cache->GetWidth(), cache->GetHeight(), 
                    cache->GetPixelFormat(), cache->GetFrameRate());
                encoder_->start(cache->GetCachePath(exportType), start);
                std::unordered_map<uint64_t, std::string> result = encoder_->run_until_finish(&forwardQueue);
                cache->CodecResultHandle(start, end, result, exportType);
                encoder_->release();
            }
        }
    }
}

ResultCode BackgroundCacheManager::start()
{
    if (running_.load()) {
        PRE_EXPORT_LOGW("already started");
        return ResultCode::OK;
    }
    forceStop();
    {
        std::lock_guard<std::mutex> lock(listMutex_);
        for (auto it = backgroundCacheList_.begin(); it != backgroundCacheList_.end();) {
            if (it->lock() == nullptr) {
                PRE_EXPORT_LOGI("remove an expired cache");
                it = backgroundCacheList_.erase(it);
            } else {
                it++;
            }
        }
    }

    running_.store(true);
    encoder_ = std::make_shared<CodecEncoder>();
    encoderThread_ = std::thread(&BackgroundCacheManager::runTask, this);
    return ResultCode::OK;
}

ResultCode BackgroundCacheManager::stop()
{
    if (!running_.load()) {
        PRE_EXPORT_LOGW("already stopped");
        return ResultCode::OK;
    }
    running_.store(false);
    if (encoderThread_.joinable()) {
        encoderThread_.join();
    }
    if (encoder_) {
        encoder_->release();
        encoder_ = nullptr;
    }
    return ResultCode::OK;
}

ResultCode BackgroundCacheManager::forceStop()
{
   running_.store(false);
    if (encoderThread_.joinable()) {
        encoderThread_.join();
    }
    if (encoder_) {
        encoder_->release();
        encoder_ = nullptr;
    }
    return ResultCode::OK;    
}